---
title: 'v8 Migration Guide'
description: Changes and new features with v8 and react 18
nav: 12
---

Work on version 8 has begun 3 Sep 2021 and is perhaps the biggest update to Fiber yet. We've tried our best to keep breaking-changes to a minimum, they mostly affect rarely used api's like `attach`. This release brings a ton of performance related fixes, but also includes some new and ground breaking features.

We would like to express our gratitude to the community for their continued support, as well as to all our contributors. 🎉

## React Native support

With React 18 and Expo 43, you can now ship threejs goodness across web and native. These apps are not confined to a web-view, they are truly native OpenGLES.

Now expo-gl, which does the hard lifting, has existed for a while, but we've addressed some of the common concerns related to interop and making the three eco system readily work in the native space. For instance, you handle assets the same exact way you'd treat them in a web app.

See [installation](/react-three-fiber/getting-started/installation#react-native) to get started with managed Expo or bare React Native apps.

```diff
- import { Canvas, useLoader } from '@react-three/fiber'
+ import { Canvas, useLoader } from '@react-three/fiber/native'

- import { useGLTF } from '@react-three/drei'
+ import { useGLTF } from '@react-three/drei/native'
```

This is complete with support for Pressability events and native threejs loaders. See [events](/react-three-fiber/api/events) for a complete list of events.

```jsx
<mesh
  onClick={(e) => console.log('onPress')}
  onPointerDown={(e) => console.log('onPressIn')}
  onPointerUp={(e) => console.log('onPressOut')}
  onDoubleClick={(e) => console.log('onLongPress')}
  onPointerOver={(e) => console.log('onHoverIn')}
  onPointerOut={(e) => console.log('onHoverOut')}
  onPointerMove={(e) => console.log('onPressMove')}
  // Not implemented
  // onContextMenu={(e) => console.log('context menu')}
  // onWheel={(e) => console.log('wheel spins')}
/>
```

```jsx
import React, { Suspense } from 'react'
import { useGLTF } from '@react-three/drei/native'
import { Canvas } from '@react-three/fiber/native'
import modelPath from './assets/model.glb'

function Model(props) {
  const { scene } = useGLTF(modelPath)
  return <primitive {...props} object={scene} />
}

export default function App() {
  return (
    <Canvas>
      <Suspense fallback={null}>
        <Model />
      </Suspense>
    </Canvas>
  )
}
```

React Native support was submitted by [@Cody_J_Bennett](https://twitter.com/Cody_J_Bennett). 🎉

## Zustand and suspend-react

Fiber uses [zustand](https://zustand.js.org/) to manage state. It's a simple, powerful, and performant state management library for React. And [suspend-react](https://github.com/pmndrs/suspend-react) to manage async ops and suspense (useLoader for instance). You can use these in your projects as well as they are to get closer interop with Fiber.

## New pixel ratio default

The default DPR has changed from `1` to `[1, 2]`, which will clamp between 1 and 2, but prefer 2, depending on the screen's native pixel ratio.

This was the most common setting in the wild, so it was brought in as a better default.

```diff
- <Canvas dpr={[1, 2]} />
+ <Canvas />
```

## Color management

Color management is now being handled by Three R139. Therefore we set `THREE.ColorManagement.enabled` to `true` and cede to touch colors and textures since everything will now be converted from sRGB to linear color space by Three itself.

You can manipulate this yourself with the `legacy` prop:

```jsx
// sets THREE.ColorManagement.enabled = false
<Canvas legacy={true}>
```

While you can of course use Fiber with any Three version you like we recommend updating to R139.

Check out https://threejs.org/docs/#manual/en/introduction/Color-management for information.

## Automatic concurrency

Concurrency is now part of React 18, which automatically switches between blocking (default) and concurrent (async).

```diff
- <Canvas mode="concurrent" />
+ <Canvas />
```

React 18 introduces the `startTransition` and `useTransition` APIs to defer and schedule expensive operations and state updates. Use these to de-prioritize expensive operations.

```jsx
import { startTransition } from 'react'
import { Points } from '@react-three/drei'

const [radius, setRadius] = useState(1)
const positions = calculatePositions(radius)
const colors = calculateColors(radius)
const sizes = calculateSizes(radius)

<Points
  positions={positions}
  colors={colors}
  sizes={sizes}
  onPointerOut={() => startTransition(() => setRadius(prev => prev + 1))}
>
  <meshBasicMaterial vertexColors />
</Points>
```

### Examples

Please be careful, this is an extreme stress test. It creates so much load that without React the browser will freeze or crash.

<p>
  <a href="https://codesandbox.io/s/qjo4t">
    <img width="49%" src="https://codesandbox.io/api/v1/sandboxes/qjo4t/screenshot.png" alt="View splitting" />
  </a>
</p>

## Conditional rendering with frameloop

`frameloop` can now be toggled to render conditionally. This is useful to toggle on user interaction or while in frame.

```jsx
const [frameloop, setFrameloop] = useState('never')

<Canvas
  frameloop={frameloop}
  onClick={() => setFrameloop('always')}
/>
```

Another usecase would be using intersection observers to stop the canvas when it's out of view.

```jsx
const canvasRef = useRef()
const [frameloop, setFrameloop] = useState('never')

useEffect(() => {
  const observer = new IntersectionObserver(([{ isIntersecting }]) => {
    setFrameloop(isIntersecting ? 'always' : 'never')
  }, {})

  observer.observe(canvasRef.current)
  return () => observer.disconnect()
}, [])

<Canvas ref={canvasRef} frameloop={frameloop} />
```

## Expanded gl prop

The `gl` prop can now accept both constructor args and renderer properties like the `camera` prop.

```jsx
<Canvas gl={{ alpha: false, physicallyCorrectLights: true }} />
```

It can also accept a synchronous callback to manually create a renderer. This allows you to use any custom renderer you want.

```jsx
<Canvas gl={(canvas) => new Renderer({ canvas })} />
```

## Improved WebXR handling

### Automatic WebXR switching

The `vr` prop was removed in favor of automatic WebXR switching. Whenever a session is requested, XR features are enabled, and the renderer will render at the native refresh rate. The inverse is true when exiting a session.

> `frameloop` will not be respected while in a session.

```diff
- <Canvas vr />
+ <Canvas />
```

### Extended useFrame

In addition to the automatic rendering, useFrame will expose the current [`XRFrame`](https://developer.mozilla.org/en-US/docs/Web/API/XRFrame) obtained via [XRSession#requestAnimationFrame](https://developer.mozilla.org/en-US/docs/Web/API/XRSession/requestAnimationFrame).

```ts
useFrame((state: RootState, delta: number, frame?: THREE.XRFrame) => { ... })
```

This removes the need for custom rendering loops when using WebXR pose data and abstractions like `useXRFrame` of [@react-three/xr](https://github.com/pmndrs/react-xr).

## Manual camera manipulation

By default Fiber is responsive and will set up cameras properly on resize (aspect ratio etc).

Cameras can be controlled manually by setting `manual` to true in `camera`. This will opt out of projection matrix recalculation when the drawing area resizes.

```jsx
<Canvas camera={{ manual: true }}>
```

This is also supported by all cameras that you create, be it a THREE.PerspectiveCamera or drei/cameras, put `manual` on it and Fiber will not touch it.

```jsx
import { PerspectiveCamera } from '@react-three/drei'
<Canvas>
  <PerspectiveCamera makeDefault manual />
</Canvas>
```

## Unified attach API

Previously, attach had multiple signatures:

- `attach="name"`
- `attachObject={["name", "attribute"]}`
- `attachArray="name"`
- `attachFns={["add", "remove"]}`
- `attachFns={[(self, parent) => parent.add(self), (self, parent) => parent.remove(self)]}`

This is now a single, unified signature with support for piercing and named attach functions or custom handlers.

```jsx
// Attach foo to parent.a
<foo attach="a" />

// Attach foo to parent.a.b and a.b.c (nested object attach)
<foo attach="a-b" />
<foo attach="a-b-c" />

// Attach foo to parent.a[0] and [1] (array attach is just object attach)
<foo attach="a-0" />
<foo attach="a-1" />

// Attach foo to parent via explicit add/remove functions
<foo attach={(parent, self) => {
  parent.add(self)
  return () => parent.remove(self)
} />

// The same as a one liner
<foo attach={(parent, self) => (parent.add(self), () => parent.remove(self))} />
```

### Real-world use-cases:

Attaching to nested objects:

```diff
- <directionalLight
-   castShadow
-   position={[2.5, 8, 5]}
-   shadow-mapSize={[1024, 1024]}
-   shadow-camera-far={50}
-   shadow-camera-left={-10}
-   shadow-camera-right={10}
-   shadow-camera-top={10}
-   shadow-camera-bottom={-10}
- />
+ <directionalLight castShadow position={[2.5, 8, 5]} shadow-mapSize={[1024, 1024]}>
+   <orthographicCamera attach="shadow-camera" args={[-10, 10, 10, -10]} />
+ </directionalLight>
```

```diff
<bufferGeometry>
-   <bufferAttribute attachObject={['attributes', 'position']} count={count} array={vertices} itemSize={3} />
+   <bufferAttribute attach="attributes-position" count={count} array={vertices} itemSize={3} />
</bufferGeometry>
```

Arrays must be explcit now:

```diff
<mesh>
-  {colors.map((color, index) => <meshBasicMaterial key={index} attachArray="material" color={color} />)}
+  {colors.map((color, index) => <meshBasicMaterial key={index} attach={`material-${index}`} color={color} />)}
</mesh>
```

## Spread Canvas props

The `<Canvas />` can now accept non-render props to spread as native props: styles, classes, events, a11y, ...

```diff
- <div aria-describedby={...}>
-  <Canvas />
- </div>
+ <Canvas aria-describedby={...} />
```

## New createRoot API

`render` is depreciated in v8 for the new `createRoot` signature.

```diff
import {
- render,
+ createRoot,
  events
} from '@react-three/fiber'

- render(<mesh />, canvas, { event })
+ createRoot(canvas).configure({ events }).render(<mesh />)
```

Here is a typical setup:

```jsx
import * as THREE from 'three'
import { extend, createRoot, events } from '@react-three/fiber'

extend(THREE)

const root = createRoot(document.querySelector('#root'))

window.addEventListener('resize', () => {
  root.configure({
    events,
    camera: { position: [0, 0, 50], fov: 50 },
    size: { width: window.innerWidth, height: window.innerHeight },
  })
  root.render(<App />)
})
window.dispatchEvent(new Event('resize'))

// This is how you would unmount the root
// root.unmount()
```

### Examples

This is a custom-renderer example using the createRoot api:

<p>
  <a href="https://codesandbox.io/s/zcuqh">
    <img width="49%" src="https://codesandbox.io/api/v1/sandboxes/zcuqh/screenshot.png" alt="View splitting" />
  </a>
</p>

## Tree-shaking via extend

The underlying reconciler no longer pulls in the THREE namespace automatically.

This enables a granular catalogue and tree-shaking via the `extend` API:

```jsx
import { extend, createRoot } from '@react-three/fiber'
import { Mesh, BoxGeometry, MeshStandardMaterial } from 'three'

extend({ Mesh, BoxGeometry, MeshStandardMaterial })

createRoot(canvas).render(
  <mesh>
    <boxGeometry />
    <meshStandardMaterial />
  </mesh>,
)
```

There's an [official babel plugin](https://github.com/pmndrs/react-three-babel) which will do this for you automatically:

```jsx
// In:

import { createRoot } from '@react-three/fiber'

createRoot(canvasNode).render(
  <mesh>
    <boxGeometry />
    <meshStandardMaterial />
  </mesh>,
)

// Out:

import { createRoot, extend } from '@react-three/fiber'
import { Mesh as _Mesh, BoxGeometry as _BoxGeometry, MeshStandardMaterial as _MeshStandardMaterial } from 'three'

extend({
  Mesh: _Mesh,
  BoxGeometry: _BoxGeometry,
  MeshStandardMaterial: _MeshStandardMaterial,
})

createRoot(canvasNode).render(
  <mesh>
    <boxGeometry />
    <meshStandardMaterial />
  </mesh>,
)
```

No changes are necessary for `@react-three/test-renderer` as THREE is extended automatically.

## createPortal creates a state enclave

`createPortal` allows you to write a declarative JSX scene into a pre-existing, foreign object. This has been very useful for portals, heads-up displays, view-cubes, view splitting, etc.

But these things were very limited and lacked event support as well as support for eco-system packages (for instance putting OrbitControls into a split view).

With this release `createPortal` creates a virtual state-model in which everything keeps functioning. Events work, you can use any 3rd party eco-system control and throw it in there.

The event layering part of this was submitted by [@theatre_js](https://twitter.com/theatre_js) and [@AndrewPrifer](https://twitter.com/AndrewPrifer) 🎉.

```jsx
import { createPortal } from '@react-three/fiber'

function HeadsUpDisplay({ children }) {
  const [scene] = useState(() => new THREE.Scene())
  return createPortal(children, scene, {
    /* Override RootState here */
  })
}
```

The event system in particular can now be layered, so that you can have portals inside portals with event priority. You can also inject objects into RootState right away, these will become the defaults inside the portalled state world and anything using `useThree` inside will receive these objects.

Here is an example of a layered portal:

```jsx
createPortal(children, scene, {
  camera: myCustomCamera,
  events: {
    priority: previousPriority - 1,
    compute: (event, state, previous) => {
      // First we call the previous state-onion-layers compute, this is what makes it possible to nest portals
      if (!previous.raycaster.camera) previous.events.compute(event, previous, previous.previousRoot.getState())
      // We run a quick check against the textured plane itself, if it isn't hit there's no need to raycast at all
      const [intersection] = previous.raycaster.intersectObject(ref.current)
      if (!intersection) return false
      // We take that hits uv coords, set up this layers raycaster, et voilà, we have raycasting with perspective shift
      const uv = intersection.uv
      state.raycaster.setFromCamera(state.pointer.set(uv.x * 2 - 1, uv.y * 2 - 1), camera)
    },
  },
})
```

`createPortal` can still be considered low-level and the exact API for `compute` is still experimental at this point. Expect ready-made components for portals, hud's and view-splitting to come to drei soon.

### Examples

<p>
  <a href="https://codesandbox.io/s/1wmlew">
    <img width="49%" src="https://codesandbox.io/api/v1/sandboxes/1wmlew/screenshot.png" alt="View splitting" />
  </a>
  <a href="https://codesandbox.io/s/kp1w5u">
    <img width="49%" src="https://codesandbox.io/api/v1/sandboxes/kp1w5u/screenshot.png" alt="Portals" />
  </a>
  <a href="https://codesandbox.io/s/dioqhj">
    <img width="49%" src="https://codesandbox.io/api/v1/sandboxes/dioqhj/screenshot.png" alt="Heads-up displays" />
  </a>
</p>

## RTTR Regex Matchers

test-renderer's `findByProps` and `findAllByProps` now accept RegExp matchers to search for variable or computed properties.

```ts
testInstance.findByProps(props)

// Also accepts RegExp matchers
testInstance.findByProps({ [prop]: /^match/i })
```

```ts
testInstance.findAllByProps(props)

// Also accepts RegExp matchers
testInstance.findAllByProps({ [prop]: /^matches/i })
```

React-three-test-renderer was submitted by [@_josh_ellis_](https://twitter.com/_josh_ellis_) 🎉.

## Deprecated

```diff
useFrame((state) => {
- state.mouse
+ state.pointer
```

```diff
onClick={(event) => {
- event.sourceEvent
+ event.nativeEvent

- event.spaceX
- event.spaceY
+ event.pointer
```
